/*
This file is part of La Linea.

La Linea is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

La Linea is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with La Linea.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "Font.h"

	int _size;
	GLuint* _VBOs;
	GLuint* _textures;

using namespace std;

// Most of this is based on NeHe #43
Font::Font(std::string name, int size){
	_VBOs = new GLuint[128];
	_textures = new GLuint[128];
	_bitmapGlyphLeft = new FT_Int[128];
	_bitmapGlyphTop = new FT_Int[128];
	_bitmapRows = new int[128];
	_glyphAdvanceX = new FT_Pos[128];
	_size = size;

	name = "fonts/"+name;

	// Create And Initilize A FreeType Font Library.
	FT_Library library;
	if (FT_Init_FreeType(&library)) throw runtime_error("FT_Init_FreeType failed");

	// Open the font file
	FT_Face face;
	if(FT_New_Face(library, name.c_str(), 0, &face)) throw runtime_error("FT_Init_FreeType failed");

	FT_Set_Char_Size(face, size*64, size*64, 72, 72); // Font unit = 1/64 pixel

	// Generate textures id
	glGenTextures(128, _textures);

	// Generate buffers for the VBOs
	glGenBuffers(128, _VBOs);
	
	// Create VBO & load textures for each glyph
	for(unsigned char charId=0;charId<128;charId++){
		// Load current char's glyph
		if(FT_Load_Char(face, charId, FT_LOAD_RENDER ))
			throw runtime_error("FT_Load_Char failed");
		
		FT_Glyph glyph;
		if(FT_Get_Glyph(face->glyph, &glyph))
			throw runtime_error("FT_Get_Glyph failed");

		// Convert glyph to bitmap
		if(FT_Glyph_To_Bitmap( &glyph, FT_RENDER_MODE_NORMAL, 0, 1 ))
			throw runtime_error("FT_Glyph_To_Bitmap failed");

		FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph)glyph;
		FT_Bitmap& bitmap=bitmap_glyph->bitmap;

		int width = Util::next_p2(bitmap.width);
		int height = Util::next_p2(bitmap.rows);

		_bitmapGlyphLeft[charId] = bitmap_glyph->left;
		_bitmapGlyphTop[charId] = bitmap_glyph->top;
		_bitmapRows[charId] = bitmap.rows;
		_glyphAdvanceX[charId] = face->glyph->advance.x;

		// Allocate memory for the texture data
		GLubyte* expanded_data = new GLubyte[2*width*height];
		memset(expanded_data, 0, 2*width*height);

		for(int j=0; j<bitmap.rows;j++) {
			for(int i=0; i<bitmap.width; i++){
				expanded_data[2*(i+j*width)] = 255;
				expanded_data[2*(i+j*width)+1] = 
					(i>=bitmap.width || j>=bitmap.rows) ? 0 : 
					bitmap.buffer[i + bitmap.width*j];
			}
		}

		// Texture parameters
		glBindTexture(GL_TEXTURE_2D, _textures[charId]);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);		
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
		glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, expanded_data );
		glBindTexture(GL_TEXTURE_2D, 0);

		delete [] expanded_data;

		// VBO
		glBindBuffer(GL_ARRAY_BUFFER, _VBOs[charId]);
		glBufferData(GL_ARRAY_BUFFER, 4*sizeof(textured2DVertex), 0, GL_STATIC_DRAW);

		float x=(float)bitmap.width/(float)width;
		float y=(float)bitmap.rows/(float)height;
		
		textured2DVertex vertex[4];

		vertex[0].x = 0.0; vertex[0].y = (GLfloat)bitmap.rows; // Vertex 0
		vertex[0].tx = 0.0; vertex[0].ty = 0.0;
		vertex[1].x = 0.0; vertex[1].y = 0.0; // Vertex 1
		vertex[1].tx = 0.0; vertex[1].ty = y;
		vertex[2].x = (GLfloat)bitmap.width; vertex[2].y = 0.0; // Vertex 2
		vertex[2].tx = x; vertex[2].ty = y;
		vertex[3].x = (GLfloat)bitmap.width; vertex[3].y = (GLfloat)bitmap.rows; // Vertex 3
		vertex[3].tx = x; vertex[3].ty = 0;

		GLvoid* vboBuffer = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
		assert (vboBuffer!=NULL);
		memcpy(vboBuffer, vertex, 4*sizeof(textured2DVertex));
		glUnmapBuffer(GL_ARRAY_BUFFER);

		FT_Done_Glyph(glyph);
	}

	// Delete temporary objects
	FT_Done_Face(face);
	FT_Done_FreeType(library);
}

Font::~Font(){
	glDeleteBuffers(128, _VBOs);
	glDeleteTextures(128,_textures);
	
	delete[] _VBOs;
	delete[] _textures;
	delete[] _bitmapGlyphLeft;
	delete[] _bitmapGlyphTop;
	delete[] _bitmapRows;
	delete[] _glyphAdvanceX;
}

void Font::drawChar(int c){
	if(c < 0 || c >= 128) return;

	if(glIsBuffer(_VBOs[c])){
		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, _textures[c]);

		glPushMatrix();
		glTranslatef((GLfloat)_bitmapGlyphLeft[c],0,0);
		glTranslatef(0,(GLfloat)_bitmapGlyphTop[c]-(GLfloat)_bitmapRows[c],0);

		glBindBuffer(GL_ARRAY_BUFFER, _VBOs[c]);
		
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		glEnableClientState(GL_VERTEX_ARRAY);

		glVertexPointer(2, GL_FLOAT, sizeof(GLfloat)*4,BUFFER_OFFSET(0));	
		glTexCoordPointer(2, GL_FLOAT, sizeof(GLfloat)*4,BUFFER_OFFSET(sizeof(GLfloat)*2));
		
		glDrawArrays(GL_QUADS, 0, 4);

		glDisableClientState(GL_VERTEX_ARRAY);
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);

		glPopMatrix();
		glTranslatef((GLfloat)(_glyphAdvanceX[c] >> 6),0,0);
	}
}